/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-12
 * V4.0
 */
package com.jphenix.share.tools;

import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.SFilesUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;

import java.io.*;
import java.util.ArrayList;
import java.util.List;


/**
 * 文件复制工具类
 * 从spring中抄出来的
 * 
 * 2018-06-02 增加了将字符串内容放入指定文件中方法copyStringToFile
 * 2018-06-11 修改了将文件复制到根路径时，截取路径字符串报错
 * 2018-06-26 如果是传参中，源文件跟目标文件都是文件对象，复制后，修改目标文件最后更新时间为源文件最后更新时间
 * 2018-09-25 突然发现这个类是抽象的，但实际上没有抽象方法，去掉了抽象关键字
 * 2019-11-15 增加了将字节数组，通过输出文件绝对路径保存的方法
 *            增加了传入文件绝对路径，返回该文件内容字节数组的方法
 * 2020-08-06 去掉了关闭流报错语句
 * @author 刘虻
 * 2007-2-1下午03:41:14
 */
@ClassInfo({"2020-08-06 15:11","文件复制工具类"})
public class FileCopyTools {
	
	public static final int BUFFER_SIZE = 4096; //段值

	/**
	 * 判断对象是否为空，如果为空则抛出指定信息异常
	 * @author 刘虻
	 * 2007-2-1下午03:42:27
	 * @param object 被判断的对象
	 * @param message 异常信息
	 */
	protected static void notNull(Object object, String message) {
		if (object == null) {
			throw new IllegalArgumentException(message);
		}
	}
	
	/**
	 * 复制文件
	 * @author 刘虻
	 * 2007-2-1下午03:59:30
	 * @param in 源文件
	 * @param out 目标文件
	 * @return 返回复制字节数
	 * @throws IOException 执行发生异常
	 */
	public static int copy(File in, File out) throws IOException {
		notNull(in, "No input File specified");
		notNull(out, "No output File specified");
		if (!out.exists()) {
			out = SFilesUtil.createFile(out.getPath());
		}
		//执行复制并获取返回值
		int res = copy(new BufferedInputStream(new FileInputStream(in)),
		    new BufferedOutputStream(new FileOutputStream(out)));
		//复制后，将复制的文件最后更新时间设置成源文件最后更新时间
		out.setLastModified(in.lastModified());
		return res;
	}
	
	
	/**
	 * 复制文件
	 * @author 刘虻
	 * 2007-11-9下午04:14:18
	 * @param inFilePath 源文件路径
	 * @param outFilePath 目标文件路径
	 * @return 返回复制字节数
	 * @throws IOException 执行发生异常
	 */
	public static int copy(
			String inFilePath,String outFilePath) throws IOException {
		return copy(new File(inFilePath),new File(outFilePath));
	}


	/**
	 * 将文件内容直接输出到流中
	 * @author 刘虻
	 * 2007-9-13上午10:12:26
	 * @param in 文件对象
	 * @param out 输出流
	 * @return 返回复制字节数
	 * @throws IOException 执行发生异常
	 */
	public static int copy(File in,OutputStream out) throws IOException {
		notNull(in, "No input File specified");
		return copy(new FileInputStream(in),out);
	}
	
	
	/**
	 * 复制文件
	 * @author 刘虻
	 * 2007-2-1下午04:00:26
	 * @param in 输入字节
	 * @param out 输出文件
	 * @throws IOException 执行发生异常
	 */
	public static void copy(byte[] in, File out) throws IOException {
		notNull(in, "No input byte array specified");
		notNull(out, "No output File specified");
		ByteArrayInputStream inStream = new ByteArrayInputStream(in);
		OutputStream outStream = new BufferedOutputStream(new FileOutputStream(out));
		copy(inStream, outStream);
	}
	
	
	/**
	 * 复制文件
	 * @param in            输入文件内容字节数组
	 * @param outFilePath   输出文件绝对路径
	 * @throws IOException  异常
	 * 2019年11月15日
	 * @author MBG
	 */
	public static void copy(byte[] in,String outFilePath) throws IOException {
		notNull(in, "No input byte array specified");
		ByteArrayInputStream inStream = new ByteArrayInputStream(in);
		copy(inStream, new File(outFilePath));
	}
	
	
	/**
	 * 复制文件
	 * @author 刘虻
	 * 2007-8-15下午07:29:20
	 * @param content 文件内容
	 * @param encoding 内容编码
	 * @param out 输出文件
	 * @throws IOException 执行发生异常
	 */
	public static void copy(
			String content,String encoding,File out) throws IOException {
		notNull(content, "No input byte array specified");
		notNull(out, "No output File specified");
		if (!out.exists()) {
			out.createNewFile();
		}
		//构造读入流
		ByteArrayInputStream inStream = null;
		if (encoding==null) {
			inStream = new ByteArrayInputStream(content.getBytes());
		}else {
			inStream = new ByteArrayInputStream(content.getBytes(encoding));
		}
		//构造输出流
		OutputStream outStream = new BufferedOutputStream(new FileOutputStream(out));
		copy(inStream, outStream);
	}


	/**
	 * 将文件读取为字节数组
	 * @author 刘虻
	 * 2007-2-1下午04:00:54
	 * @param in 源文件
	 * @return 读取的字节数组
	 * @throws IOException 执行发生异常
	 */
	public static byte[] copyToByteArray(File in) throws IOException {
		notNull(in, "No input File specified");
		return copyToByteArray(new BufferedInputStream(new FileInputStream(in)));
	}
	
	/**
	 * 将指定文件读取为字节数组
	 * @param filePath 文件绝对路径
	 * @return 文件内容字节数组
	 * @throws IOException 异常
	 * 2019年11月15日
	 * @author MBG
	 */
	public static byte[] copyToByteArray(String filePath) throws IOException {
		File in = new File(filePath); //构建读取文件对象
		notNull(in, "No input File specified");
		return copyToByteArray(new BufferedInputStream(new FileInputStream(in)));
	}

	
	/**
	 * 复制文件
	 * @param in             源文件读入流
	 * @param outFilePath    目标文件路径
	 * @return               复制字节数
	 * @throws IOException   异常
	 * 2016年12月9日
	 * @author MBG
	 */
	public static int copy(InputStream in,String outFilePath) throws IOException {
		//输出文件对象
		File outFile = new File(outFilePath);
		if (!outFile.exists()) {
			outFile = SFilesUtil.createFile(outFile.getPath());
		}
		return copy(in,new BufferedOutputStream(new FileOutputStream(outFile)));
	}
	
	/**
	 * 复制文件
	 * @param in             源文件读入流
	 * @param outFile    目标文件对象
	 * @return               复制字节数
	 * @throws IOException   异常
	 * 2016年12月9日
	 * @author MBG
	 */
	public static int copy(InputStream in,File outFile) throws IOException {
		notNull(outFile, "No OutputFile specified");
		if (!outFile.exists()) {
			SFilesUtil.createFile(outFile.getPath());
		}
		return copy(in,new BufferedOutputStream(new FileOutputStream(outFile)));
	}

	/**
	 * 复制文件
	 * @author 刘虻
	 * 2007-2-1下午04:02:34
	 * @param in 源文件输入流
	 * @param out 目标文件输出流
	 * @return 复制字节数
	 * @throws IOException 执行发生异常
	 */
	public static int copy(InputStream in, OutputStream out) throws IOException {
		notNull(in, "No InputStream specified");
		notNull(out, "No OutputStream specified");
		try {
			int byteCount = 0;
			byte[] buffer = new byte[BUFFER_SIZE];
			int bytesRead = -1;
			while ((bytesRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, bytesRead);
				byteCount += bytesRead;
			}
			out.flush();
			return byteCount;
		}finally {
			try {
				in.close();
			}catch (IOException ex) {}
			try {
				out.close();
			}catch (IOException ex) {}
		}
	}


	/**
	 * 复制文件
	 * @author 刘虻
	 * 2007-2-1下午04:03:15
	 * @param in 源文件字节数组
	 * @param out 目标文件输出流
	 * @throws IOException 执行发生异常
	 */
	public static void copy(byte[] in, OutputStream out) throws IOException {
		notNull(in, "No input byte array specified");
		notNull(out, "No OutputStream specified");
		try {
			out.write(in);
		}
		finally {
			try {
				out.close();
			}catch (IOException ex) {}
		}
	}


	/**
	 * 将文件读出到字节数组中
	 * @author 刘虻
	 * 2007-2-1下午04:04:43
	 * @param in 源文件输入流
	 * @return 读取的字节数组
	 * @throws IOException 执行发生异常
	 */
	public static byte[] copyToByteArray(InputStream in) throws IOException {
		ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
		copy(in, out);
		return out.toByteArray();
	}


	/**
	 * 将文件读入字符串
	 * @author 刘虻
	 * 2007-8-15下午07:27:29
	 * @param in 读入文件
	 * @param encoding 输出编码
	 * @return 返回读出文件内容
	 * @throws IOException 执行发生异常
	 */
	public static String copyToString(File in,String encoding) throws IOException{
		ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
		copy(new BufferedInputStream(new FileInputStream(in)),out);
		if (encoding==null) {
			return out.toString();
		}
		return out.toString(encoding);
	}
	
	
	/**
	 * 将文件读取为字符串
	 * @author 刘虻
	 * 2007-2-1下午04:00:54
	 * @param in 读取流
	 * @return 读取的字符串
	 * @throws IOException 执行发生异常
	 */
	public static String copyToString(InputStream in) throws IOException {
		notNull(in, "No input File specified");
		ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
		copy(in, out); //执行复制
		return out.toString();
	}
	
	
	   /**
     * 将文件读取为字符串
     * @author 刘虻
     * 2007-2-1下午04:00:54
     * @param in 读取流
     * @param enc 输出编码格式
     * @return 读取的字符串
     * @throws IOException 执行发生异常
     */
    public static String copyToString(InputStream in,String enc) throws IOException {
        notNull(in, "No input File specified");
        ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
        copy(in, out); //执行复制
        if(enc!=null) {
            return out.toString(enc);
        }
        return out.toString();
    }

	
	/**
	 * 复制文件
	 * @author 刘虻
	 * 2007-2-1下午04:11:33
	 * @param in 源文件读取类
	 * @param out 目标文件读取类
	 * @return 读取字节数
	 * @throws IOException 执行发生异常
	 */
	public static int copy(Reader in, Writer out) throws IOException {
		notNull(in, "No Reader specified");
		notNull(out, "No Writer specified");
		try {
			int byteCount = 0;
			char[] buffer = new char[BUFFER_SIZE];
			int bytesRead = -1;
			while ((bytesRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, bytesRead);
				byteCount += bytesRead;
			}
			out.flush();
			return byteCount;
		}
		finally {
			try {
				in.close();
			}catch (IOException ex) {}
			try {
				out.close();
			}catch (IOException ex) {}
		}
	}


	/**
	 * 将字符串写入文件
	 * @author 刘虻
	 * 2007-2-1下午04:13:14
	 * @param in 源字符串
	 * @param out 目标文件输出类
	 * @throws IOException 执行发生异常
	 */
	public static void copy(String in, Writer out) throws IOException {
		notNull(in, "No input String specified");
		notNull(out, "No Writer specified");
		try {
			out.write(in);
		}
		finally {
			try {
				out.close();
			}catch (IOException ex) {}
		}
	}


	/**
	 * 将源文件读取到字符串中
	 * @author 刘虻
	 * 2007-2-1下午04:12:12
	 * @param in 源文件读取类
	 * @return 读取后的字符串
	 * @throws IOException 执行发生异常
	 */
	public static String copyToString(Reader in) throws IOException {
		StringWriter out = new StringWriter();
		copy(in, out);
		return out.toString();
	}
	
	
	/**
	 * 获取准备复制的文件列表
	 * @author 刘虻
	 * 2008-9-10下午05:22:36
	 * @param srcPath 文件根路径
	 * @param filePathList 文件路径序列
	 * @throws Exception 执行发生异常
	 */
	public static void getCopyFileList(String srcPath,List<String> filePathList) throws Exception {
		if (srcPath==null 
				|| srcPath.length()<1
				|| filePathList==null) {
			return;
		}
		//构建源文件对象
		File srcFile = new File(srcPath);
		if (!srcFile.exists()) {
			throw new Exception("Not Find The Resource File Path:["+srcPath+"]");
		}
		srcPath = BaseUtil.swapString(srcPath,"\\","/");
		if (!srcPath.endsWith("/")) {
			srcPath += "/";
		}
		//执行搜索
		doSetFilePath(filePathList,srcPath,null);
	}
	
	/**
	 * 将源文件夹复制到目标文件夹中
	 * @author 刘虻
	 * 2008-9-10下午03:08:21
	 * @param srcPath 原文件夹路径
	 * @param objPath 目标文件夹路径
	 * @throws Exception 执行发生异常
	 */
	public static void copyFilePath(String srcPath,String objPath,String objName) throws Exception {
		
		if (srcPath==null 
				|| srcPath.length()<1 
				|| objPath==null 
				|| objPath.length()<1) {
			return;
		}
		//构建源文件对象
		File srcFile = new File(srcPath);
		if (!srcFile.exists()) {
			throw new Exception("Not Find The Resource File Path:["+srcPath+"]");
		}
		if (srcFile.isFile()) {
			copyFileToPath(srcFile,objPath,objName);
			return;
		}
		objPath = BaseUtil.swapString(objPath,"\\","/");
		if (!objPath.endsWith("/")) {
			objPath += "/";
		}
		//构建文件路径序列
		ArrayList<String> fileList = new ArrayList<String>();
		//执行搜索
		getCopyFileList(srcPath,fileList);
		
		for(String pathElement:fileList) {
			if (pathElement==null || pathElement.length()<1) {
				continue;
			}
			//构建目标路径
			String objPathElement = "";
			//获取路径分割点
			int point = pathElement.lastIndexOf("/");
			if (point>-1) {
				objPathElement = pathElement.substring(0,point);
			}
			//执行复制文件
			copyFileToPath(new File(srcPath+pathElement),objPath+objPathElement,null);
		}
	}
	
	
	/**
	 * 将源文件复制到目标文件夹中
	 * @author 刘虻
	 * 2008-9-10下午03:14:38
	 * @param srcFile 源文件对象
	 * @param objPath 目标文件夹路径
	 * @param objName 目标文件名 默认为源文件名
	 * @throws Exception 执行发生异常
	 */
	public static void copyFileToPath(File srcFile,String objPath,String objName) throws Exception {
		if (srcFile==null 
				|| !srcFile.exists() 
				|| objPath==null 
				|| objPath.length()<1) {
			return;
		}
		objPath = BaseUtil.swapString(objPath,"\\","/");
		if (!objPath.endsWith("/")) {
			objPath += "/";
		}
		//构建目标路径对象
		File objPathObj = new File(objPath);
		if (!objPathObj.exists()) {
			objPathObj.mkdirs();
		}
		if(objName==null || objName.length()<1) {
			objName = srcFile.getName();
		}
		//执行复制
		copy(srcFile,new File(objPath+objName));
	}
	
	
	/**
	 * 将源文件复制到目标文件夹中
	 * @author 刘虻
	 * 2008-9-10下午03:14:38
	 * @param srcFile 源文件对象
	 * @param objPath 目标文件路径（全路径）
	 * @throws Exception 执行发生异常
	 */
	public static void copyFileToPath(File srcFile,String objPath) throws Exception {
		if (srcFile==null 
				|| !srcFile.exists() 
				|| objPath==null 
				|| objPath.length()<1) {
			return;
		}
		//目标文件夹路径
		String filePath = BaseUtil.swapString(objPath,"\\","/");
		if (!filePath.endsWith("/")) {
			int point = filePath.lastIndexOf("/");
			if(point>0) {
				filePath = filePath.substring(0,point);
			}
		}
		//文件所在文件夹对象
		File pathFile = new File(filePath);
		if(!pathFile.exists()) {
			pathFile.mkdirs();
		}
		//构建目标路径对象
		File objPathObj = new File(objPath);
		if (!objPathObj.exists()) {
			objPathObj.mkdirs();
		}
		//执行复制
		copy(srcFile,new File(objPath));
	}
	
	
	
	/**
	 * 将源文件复制到目标文件夹中
	 * @author 刘虻
	 * 2008-9-10下午03:14:38
	 * @param is 源文件读入流
	 * @param objPath 目标文件全路径
	 * @throws Exception 执行发生异常
	 */
	public static void copyFileToPath(InputStream is,String objPath) throws Exception {
		if (is==null 
				|| objPath==null 
				|| objPath.length()<1) {
			return;
		}
		//目标文件夹路径
		String filePath = BaseUtil.swapString(objPath,"\\","/");
		if (!filePath.endsWith("/")) {
			int point = filePath.lastIndexOf("/");
			if(point>0) {
				filePath = filePath.substring(0,point);
			}
		}
		//文件所在文件夹对象
		File pathFile = new File(filePath);
		if(!pathFile.exists()) {
			pathFile.mkdirs();
		}
		//执行复制
		copy(is,new File(objPath));
	}
	
	
	/**
	 * 将内容写入文件
	 * @author 刘虻
	 * 2009-3-3下午12:24:25
	 * @param content 文件内容
	 * @param encoding 内容编码
	 * @param outFilePath 输出文件路径
	 * @param isAppend 是否为添加
	 */
	public static void copyToFile(String content,String encoding,String outFilePath,boolean isAppend) {
		copyToFile(content,encoding,new File(outFilePath),isAppend);
	}
	
	/**
	 * 将内容写入文件
	 * @author 刘虻
	 * 2009-3-3下午12:18:59
	 * @param content 文件内容
	 * @param encoding 内容编码
	 * @param outFile 输出文件
	 * @param isAppend 是否为添加
	 */
	public static void copyToFile(String content,String encoding,File outFile,boolean isAppend) {
		
		if (content==null || content.length()<1) {
			return;
		}
		FileOutputStream fops = null; //构建文件写入流
		try {
			fops = new FileOutputStream(outFile,isAppend);
			
			if (encoding==null) {
				fops.write(content.getBytes());
			}else {
				fops.write(content.getBytes(encoding));
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			try {
				fops.close();
			}catch(Exception e2) {}
		}
	}
	
	
	  /**
	 * 获得指定路径下，符合文件类型的所有文件路径
	 * @author 刘虻
	 * @param filePathArrl 存放搜索后的文件路径
	 * @param basePathStr 指定根文件夹路径（末尾不加/)
	 * Exception 获得文件路径失败
	 * 2006-4-1  11:26:00
	 */
    protected static void doSetFilePath (
            List<String> filePathArrl
            ,String basePathStr
            ,String subPath) throws Exception {
    	if (subPath==null) {
    		subPath = "";
    	}else if(!subPath.endsWith("/")){
    		subPath += "/";
    	}
    	//构建根路径对象
    	File file = new File(basePathStr+subPath);
        //获得当前路径下，所有符合条件的文件和文件夹
        String[] filePathListStrs = 
        	file.list(new FilenameFilter() {
            //文件筛选类
            @Override
            public boolean accept(File dirPathFile, String lastNameStr) {
            	return true;
            }
        });
        
        //判断是否存在文件或文件夹
        if (filePathListStrs != null && filePathListStrs.length >0) {
            //循环筛选
            for ( int i=0; i< filePathListStrs.length; i++){
            	//构建相对路径
            	filePathListStrs[i] = subPath+filePathListStrs[i];
                //构建查询到的文件或路径
                String findFilePath = basePathStr+filePathListStrs[i];
                //构造查询到的文件或文件夹
                File findFile = new File(findFilePath);
                if (findFile.isDirectory()) {
                    //如果是文件夹，并且搜索子文件夹，则递归调用方法
                	doSetFilePath(filePathArrl,basePathStr,filePathListStrs[i]);
                }else {
                    //加入文件路径
                    filePathArrl.add("/"+filePathListStrs[i]);
                }
    	    }
        }
    }
    
    
    /**
     * 将字符串内容保存到指定文件中
     * @param content   字符串内容 (UTF-8格式）
     * @param out       输出文件对象
     * @param append    是否将字符串添加到文件末尾
     * @return          执行是否成功
     * 2018年6月2日
     * @author MBG
     */
    public static boolean copyStringToFile(
    		String content,File out,boolean append) {
    	return copyStringToFile(content,"UTF-8",out,append);
    }

    /**
     * 将字符串内容保存到指定文件中
     * @param content   字符串内容
     * @param encoding  字符串编码格式
     * @param out       输出文件对象
     * @param append    是否将字符串添加到文件末尾
     * @return          执行是否成功
     * 2018年6月2日
     * @author MBG
     */
    public static boolean copyStringToFile(
    		String content,String encoding,File out,boolean append) {
    	FileOutputStream os = null; //文件写入流
    	try {
    		if(content==null || content.length()<1) {
    			return false;
    		}
    		if (!out.exists()) {
    			//可能需要多个没有建立的子文件夹，通过该方法建立
    			SFilesUtil.createFile(out.getPath());
    		}
    		byte[] arrs; //写入内容字节数组
    		if(encoding==null ||encoding.length()<1) {
    			arrs = content.getBytes();
    		}else {
    			arrs = content.getBytes(encoding);
    		}
    		os = new FileOutputStream(out,append);
    		//执行写入
			return copy(new ByteInputStream(arrs, arrs.length), os) >= 0;
		}catch(Exception e) {
    		e.printStackTrace();
    	}finally {
    		if(os!=null) {
    			try {
    				os.close();
    			}catch(Exception e2) {}
    			os = null;
    		}
    	}
    	return false;
    }
}
